package com.projectbox.service.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.projectbox.model.Resource;
import com.projectbox.model.Folder;
import com.projectbox.service.ResourceService;
import com.projectbox.dao.UserDao;
import com.projectbox.dao.ResourceDao;
import com.projectbox.dao.ProjectDao;
import com.projectbox.utility.ResourceStorageUtility;
import com.projectbox.utility.ResourceIconUtility;

public class ResourceFolderServiceImpl implements ResourceService {

	private Logger logger = LoggerFactory.getLogger(getClass());

	private ResourceDao resourceDao;
	private UserDao userDao;
	private ProjectDao projectDao;
	private ResourceStorageUtility resourceStorageUtility;
	private ResourceIconUtility resourceIconUtility;

	public void setResourceDao(ResourceDao resourceDao) {
		this.resourceDao = resourceDao;
	}

	public void setResourceIconUtility(ResourceIconUtility resourceIconUtility){
		this.resourceIconUtility = resourceIconUtility;
	}
	public void setResourceStorageUtility(
			ResourceStorageUtility resourceStorageUtility) {
		this.resourceStorageUtility = resourceStorageUtility;
	}

	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public void setProjectDao(ProjectDao projectDao) {
		this.projectDao = projectDao;
	}

	public Resource findResource(Long id) {
		Resource resource = this.resourceDao.getResourceById(id);

		if (resource != null) {
			byte[] file = resourceStorageUtility.getResourceFile(resource);
			resource.setFile(file);
		}

		return resource;
	}

	public void addResource(byte[] fileContent, String name,
			String description, Long uid, Long pid, Long fid) {
		Resource resource = new Resource();
		if ((resourceDao.checkResourceName(fid, name) != null)){
			name = getSuggestResourceName(name, fid);
		}
		resource.setName(name);
		resource.setDescription(description);
		logger.debug("type"+getResourceType(name));
		resource.setType(getResourceType(name));
		resource.setDescription(description);
		resource.setPath(pid + "/" + name);
		resource.setFile(fileContent);
		
		Calendar cal = Calendar.getInstance();       // get calendar instance
		cal.set(Calendar.HOUR_OF_DAY, 0);            // set hour to midnight
		cal.set(Calendar.MINUTE, 0);                 // set minute in hour
		cal.set(Calendar.SECOND, 0);                 // set second in minute
		cal.set(Calendar.MILLISECOND, 0);            // set millis in second
		
		long time = System.currentTimeMillis();
		resource.setTimeCreated(cal.getTimeInMillis());
		resource.setTimeModified(time);

		resource.setUid(uid);
		resource.setPid(pid);
		resource.setFid(fid);
		resource.setLastModId(uid);

		this.resourceDao.insert(resource);
		this.resourceStorageUtility.saveResource(resource);
	}

	public List<Resource> findAllProjectResources(Long pid) {
		return this.setProperties(this.resourceDao.getAllProjectResources(pid));
	}
	
	public List<Resource> findProjectResources(Long pid) {
		return this.setProperties(this.resourceDao.getProjectResources(pid));
	}

	public List<Resource> findProjectResourcesByUserId(Long uid, Long pid) {
		return this.setProperties(this.resourceDao.getProjectResourcesByUserId(
				uid, pid));
	}

	public void removeResource(Resource resource) {
		try {
			this.resourceStorageUtility.deleteResource(resource);
		} catch (Exception e) {
			logger.debug("exception on delete " + resource.getName() + " " + e.getClass().getName());
		}
		this.resourceDao.delete(resource.getId());
	}

	private List<Resource> setProperties(List<Resource> resourceList) {
		if (resourceList != null) {
			for (Resource r : resourceList) {
				r.setOwner(this.userDao.getUserById(r.getUid()));
				r.setProject(this.projectDao.getProjectById(r.getPid()));
				r.setLastModifier(this.userDao.getUserById(r.getLastModId()));
			}
		}
		return resourceList;
	}

	

	public void deleteFolder(Long fid) {
		// TODO delete files in sub folder from file system
		List<Resource> resources = resourceDao.getResourcesByFolder(fid);
		logger.debug("folder resource " + resources);
		for (Resource r : resources) {
			try {
				resourceStorageUtility.deleteResource(r);
			} catch (Exception e) {
				logger.debug("exception on delete " + r.getName() + " " + e.getClass().getName());
			}
		}
		List<Folder> folders = findSubfolders(fid);
		for (Folder f : folders) {
			if (!findSubfolders(f.getId()).isEmpty()) {
				this.deleteFolder(f.getId());
			}
			for (Resource r : resourceDao.getResourcesByFolder(f.getId())) {
				try {
					resourceStorageUtility.deleteResource(r);
				} catch (Exception e) {
					logger.debug("exception on delete " + r.getName() + " " + e.getClass().getName());
				}
			}
			
			this.resourceDao.deleteFolder(f.getId());
		}
		
		this.resourceDao.deleteFolder(fid);
	}

	public Folder findFolder(Long fid) {
		return this.resourceDao.getFolderById(fid);
	}

	public List<Resource> findResourcesByFolder(Long fid) {
		return this.setProperties(this.resourceDao.getResourcesByFolder(fid));
	}

	public List<Folder> findSubfolders(Long fid) {
		return this.resourceDao.getSubfolders(fid);
	}

	public List<Folder> findSubfoldersByPid(Long pid) {
		return this.resourceDao.getSubfoldersByPid(pid);
	}

	public List<Folder> findPreFoldersById(Long fid) {
		List<Folder> traceFolders = new ArrayList<Folder>();
		Folder currentFolder = this.resourceDao.getFolderById(fid);
		while (currentFolder.getPreId() != 0) {
			currentFolder = resourceDao.getFolderById(currentFolder.getPreId());
			traceFolders.add(currentFolder);
		}
		Collections.reverse(traceFolders);
		return traceFolders;
	}

	public List<Resource> findResourcesByUserId(Long uid) {
		return this.setProperties(this.resourceDao.getResourcesByUserId(uid));
	}

	public Map<String, String> renameResource(Long rid, String newName) {
		Resource resource = this.resourceDao.getResourceById(rid);
		Map<String, String> names = new HashMap<String, String>();
		names.put("type", getResourceType(newName));
		names.put("id", resource.getId().toString());
		names.put("pid", resource.getPid().toString());
		names.put("oldName", resource.getName());
		logger.debug(resource.getName());
		if (resourceDao.checkResourceName(resource.getFid(), newName) != null) {
			logger.debug("resource name exists");
			names.put("suggestName", getSuggestResourceName(newName, resource.getFid()));
		} else {
			logger.debug("no that name");
			this.resourceDao.renameResource(rid, newName, getResourceType(newName));
			this.resourceStorageUtility.renameResource(resource, newName);			
			names.put("suggestName", null);
		}
		return names;
	}
	
	public void createNewFolder(String name, Long preId, Long pid) {
		Folder folder = new Folder();
		if ((resourceDao.checkFolderName(preId, name) != null)){
			name = getSuggestFolderName(name, preId);
		}
		folder.setName(name);
		folder.setPreId(preId);
		folder.setPid(pid);
		this.resourceDao.insertFolder(folder);
	}

	public Map<String, String> renameFolder(Long fid, String newName) {

		Folder folder = this.resourceDao.getFolderById(fid);
		Map<String, String> names = new HashMap<String, String>();
		names.put("type", "folder");
		names.put("id", folder.getId().toString());
		names.put("pid", folder.getPid().toString());
		names.put("oldName", folder.getName());
		logger.debug(folder.getName());
		if (resourceDao.checkFolderName(folder.getPreId(), newName) != null) {
			logger.debug("folder name suggest: "+ getSuggestFolderName(newName, folder.getPreId()));
			names.put("suggestName", getSuggestFolderName(newName, folder.getPreId()));
		} else {
			this.resourceDao.renameFolder(fid, newName);
			names.put("suggestName", null);
		}
		return names;
	}

	public String getInitialFolderName(String name, Long preId) {
		if (resourceDao.checkFolderName(preId, name) != null) {
			return getSuggestFolderName(name, preId);			
		} else {
			return name;
		}		
	}
	
	private String getSuggestFolderName(String name, Long preId) {
		String suggestName = null;
		suggestName = getVersionNumber(name);
		if ((resourceDao.checkFolderName(preId, suggestName) != null))
			suggestName = getSuggestFolderName(suggestName, preId);
		return suggestName;
	}

	private String getSuggestResourceName(String name, Long fid) {
		// checkID is preID for folder or fid for resource
		String[] temp;
		String suggestName = null;
		String delimiter = "\\.";
		temp = name.split(delimiter);
		if (temp.length <= 1) {
			suggestName = getVersionNumber(name);
		} else {
			int typeLength = temp[temp.length - 1].length();
			String nameWithoutType = name.substring(0, name.length()
					- typeLength - 1);
			suggestName = getVersionNumber(nameWithoutType) + "."
					+ temp[temp.length - 1];
		}

		if (resourceDao.checkResourceName(fid, suggestName) != null)
			suggestName = getSuggestResourceName(suggestName, fid);

		return suggestName;
	}
	
	private String getResourceType(String name){
		String[] temp = name.split("\\.");
		if(temp.length <= 1){
			return "file";
		} else {
			String type = resourceIconUtility.getResourceType(temp[temp.length - 1]);
			return (type != null) ? type : "file";
		}
	}

	private String getVersionNumber(String name) {

		Pattern pattern = Pattern.compile("\\(\\d+\\)$");
		Matcher m = pattern.matcher(name.trim());
		if (m.find()) {
			int increase = Integer.parseInt(m.group().substring(1,
					m.group().length() - 1));
			increase++;
			name = name.replaceFirst("\\(\\d+\\)$", "(" + increase + ")");
		} else {
			name += "(1)";
		}
		return name;
	}
	
	public List<Resource> getLatestResources(Long uid){
		return setProperties(resourceDao.getLatestResources(uid));
		
	}
}
